
#ifndef AFTERMATH_PROBABILITY_DIST_UNIFORM_HPP_INCLUDED
#define AFTERMATH_PROBABILITY_DIST_UNIFORM_HPP_INCLUDED

#include "../double_constants.hpp"
#include "dist_traits.hpp"

#include <string>
#include <utility>

namespace aftermath
{
    namespace probability
    {
        // Forward declaration to define traits.
        struct dist_uniform;

        template <>
        struct is_continuous<dist_uniform>
        {
            typedef dist_uniform distribution_type;

            static const bool value = true;
        };

        struct dist_uniform
        {
            typedef dist_uniform type;
            typedef double result_type;

            static const std::string name;

        private:
            double m_a, m_b;
            double m_cache_expected_value, m_cache_variance, m_cache_standard_deviation;
            double m_cache_length, m_cache_density;

        public:
            dist_uniform()
                : m_a(0.0), m_b(1.0),
                m_cache_expected_value(0.5), m_cache_variance(double_constants::one_over_twelwe), m_cache_standard_deviation(double_constants::one_over_root_twelwe),
                m_cache_length(1.0), m_cache_density(1.0)
            {
            }

            explicit dist_uniform(double a = 0.0, double b = 1.0)
                : m_a(a), m_b(b),
                m_cache_expected_value((a + b) / 2), m_cache_variance((b - a) * (b - a) / 12), m_cache_standard_deviation((b - a) / double_constants::root_twelwe),
                m_cache_length(b - a), m_cache_density(1 / (b - a))
            {
            }

            double min() const { return this->m_a; }
            double max() const { return this->m_b; }

            double expected_value() const { return this->m_cache_expected_value; }
            double variance() const { return this->m_cache_variance; }
            double standard_deviation() const { return this->m_cache_standard_deviation; }

            double mean() const { return this->expected_value(); }
            double stddev() const { return this->standard_deviation(); }

            double cdf(double x) const { return x < this->m_a ? 0.0 : (x >= this->m_b ? 1.0 : ((x - this->m_a) / this->m_cache_length)); }
            double pdf(double x) const { return x < this->m_a ? 0.0 : (x > this->m_b ? 0.0 : this->m_cache_density); }

            bool operator ==(const type& other) const
            {
                return
                    this->m_a == other.m_a &&
                    this->m_b == other.m_b;
            }

            bool operator !=(const type& other) const
            {
                return !this->operator==(other);
            }
        };
        
        const std::string dist_uniform::name = "uniform";
    }
}

namespace std
{
    template <>
    struct hash<aftermath::probability::dist_uniform>
    {
        typedef aftermath::probability::dist_uniform argument_type;
        typedef std::size_t result_type;

        result_type operator()(argument_type const& x) const
        {
            return std::hash<double>()(x.min()) ^ std::hash<double>()(x.max());
        }
    };
}

#endif // AFTERMATH_PROBABILITY_DIST_UNIFORM_HPP_INCLUDED
